<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Img2Img 接口测试</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }

        .header p {
            font-size: 1.1em;
            opacity: 0.9;
        }

        .content {
            padding: 30px;
        }

        .form-section {
            margin-bottom: 30px;
            padding: 20px;
            border: 1px solid #e0e0e0;
            border-radius: 10px;
            background: #f9f9f9;
        }

        .form-section h3 {
            color: #333;
            margin-bottom: 15px;
            font-size: 1.3em;
        }

        .form-row {
            display: flex;
            gap: 20px;
            margin-bottom: 15px;
            align-items: center;
        }

        .form-group {
            flex: 1;
        }

        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
            color: #555;
        }

        .form-group input, .form-group select, .form-group textarea {
            width: 100%;
            padding: 10px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            transition: border-color 0.3s;
        }

        .form-group input:focus, .form-group select:focus, .form-group textarea:focus {
            outline: none;
            border-color: #667eea;
        }

        .image-upload {
            border: 2px dashed #ddd;
            border-radius: 10px;
            padding: 30px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
            background: white;
        }

        .image-upload:hover {
            border-color: #667eea;
            background: #f0f4ff;
        }

        .image-upload.dragover {
            border-color: #667eea;
            background: #f0f4ff;
        }

        .image-preview {
            max-width: 200px;
            max-height: 200px;
            margin: 10px auto;
            border-radius: 5px;
            display: none;
        }

        .btn {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 12px 30px;
            border-radius: 25px;
            font-size: 16px;
            cursor: pointer;
            transition: all 0.3s;
            font-weight: 600;
        }

        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
        }

        .btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }

        .btn-secondary {
            background: #6c757d;
        }

        .btn-secondary:hover {
            box-shadow: 0 10px 20px rgba(108, 117, 125, 0.3);
        }

        .result-section {
            margin-top: 30px;
            padding: 20px;
            border: 1px solid #e0e0e0;
            border-radius: 10px;
            background: #f9f9f9;
            display: none;
        }

        .result-section h3 {
            color: #333;
            margin-bottom: 15px;
        }

        .result-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-bottom: 20px;
        }

        .result-item {
            background: white;
            padding: 15px;
            border-radius: 8px;
            border: 1px solid #e0e0e0;
        }

        .result-item h4 {
            color: #333;
            margin-bottom: 10px;
        }

        .result-item p {
            color: #666;
            word-break: break-all;
        }

        .image-result {
            text-align: center;
            margin-top: 20px;
        }

        .generated-image {
            max-width: 100%;
            max-height: 400px;
            border-radius: 8px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
        }

        .loading {
            text-align: center;
            padding: 40px;
            color: #666;
        }

        .spinner {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #667eea;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
            margin: 0 auto 20px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .error {
            background: #f8d7da;
            color: #721c24;
            padding: 15px;
            border-radius: 5px;
            margin: 10px 0;
            border: 1px solid #f5c6cb;
        }

        .success {
            background: #d4edda;
            color: #155724;
            padding: 15px;
            border-radius: 5px;
            margin: 10px 0;
            border: 1px solid #c3e6cb;
        }

        .button-group {
            display: flex;
            gap: 15px;
            justify-content: center;
            margin-top: 20px;
        }

        @media (max-width: 768px) {
            .form-row {
                flex-direction: column;
                gap: 10px;
            }
            
            .button-group {
                flex-direction: column;
            }
            
            .header h1 {
                font-size: 2em;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🖼️ Img2Img 接口测试</h1>
            <p>上传图片并生成新的图像</p>
        </div>

        <div class="content">
            <!-- 图片上传区域 -->
            <div class="form-section">
                <h3>📁 上传初始图片</h3>
                <div class="image-upload" id="imageUpload">
                    <div id="uploadText">
                        <p>点击选择图片或拖拽图片到此处</p>
                        <p style="font-size: 12px; color: #666; margin-top: 10px;">
                            支持 JPG、PNG 格式，建议尺寸 512x512 或 768x768
                        </p>
                    </div>
                    <img id="imagePreview" class="image-preview" alt="预览图">
                </div>
                <input type="file" id="fileInput" accept="image/*" style="display: none;">
            </div>

            <!-- 参数设置区域 -->
            <div class="form-section">
                <h3>⚙️ 生成参数设置</h3>
                
                <div class="form-row">
                    <div class="form-group">
                        <label for="modelName">模型风格 *</label>
                        <select id="modelName" required>
                            <option value="anime_style">动漫风格</option>
                            <option value="ghibli_style">宫崎骏风格</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="prompt">正面提示词 *</label>
                        <input type="text" id="prompt" placeholder="例如：a beautiful cat" required>
                    </div>
                </div>

                <div class="form-row">
                    <div class="form-group">
                        <label for="negativePrompt">负面提示词</label>
                        <input type="text" id="negativePrompt" placeholder="例如：ugly, blurry, low quality">
                    </div>
                    <div class="form-group">
                        <label for="denoisingStrength">去噪强度</label>
                        <input type="number" id="denoisingStrength" min="0" max="1" step="0.1" value="0.75">
                    </div>
                </div>

                <div class="form-row">
                    <div class="form-group">
                        <label for="width">图片宽度</label>
                        <input type="number" id="width" min="64" max="2048" value="512">
                    </div>
                    <div class="form-group">
                        <label for="height">图片高度</label>
                        <input type="number" id="height" min="64" max="2048" value="512">
                    </div>
                </div>

                <div class="form-row">
                    <div class="form-group">
                        <label for="steps">步数</label>
                        <input type="number" id="steps" min="1" max="150" value="30">
                    </div>
                    <div class="form-group">
                        <label for="cfgScale">CFG 比例</label>
                        <input type="number" id="cfgScale" min="1" max="30" step="0.1" value="7.0">
                    </div>
                </div>

                <div class="form-row">
                    <div class="form-group">
                        <label for="seed">种子值</label>
                        <input type="number" id="seed" placeholder="留空使用随机种子">
                    </div>
                    <div class="form-group">
                        <label for="samplerName">采样器</label>
                        <select id="samplerName">
                            <option value="DPM++ 2M">DPM++ 2M</option>
                            <option value="Euler">Euler</option>
                            <option value="Euler a">Euler a</option>
                            <option value="DDIM">DDIM</option>
                        </select>
                    </div>
                </div>
            </div>

            <!-- 操作按钮 -->
            <div class="button-group">
                <button class="btn" id="generateBtn" onclick="generateImage()">🚀 开始生成</button>
                <button class="btn btn-secondary" onclick="previewParameters()">👁️ 预览参数</button>
                <button class="btn btn-secondary" onclick="resetForm()">🔄 重置表单</button>
                <button class="btn btn-secondary" onclick="testHealth()">💓 健康检查</button>
            </div>

            <!-- 加载状态 -->
            <div id="loading" class="loading" style="display: none;">
                <div class="spinner"></div>
                <p>正在生成图片，请稍候...</p>
            </div>

            <!-- 参数预览区域 -->
            <div id="previewSection" class="result-section" style="display: none;">
                <h3>🔍 参数预览</h3>
                <div id="previewContent"></div>
            </div>

            <!-- 结果显示区域 -->
            <div id="resultSection" class="result-section">
                <h3>📊 生成结果</h3>
                <div id="resultContent"></div>
            </div>
        </div>
    </div>

    <script>
        // 全局变量
        let selectedImage = null;
        // 修复API地址，使用固定地址而不是当前域名
        const API_BASE_URL = 'http://localhost:8088';
        const API_ENDPOINT = '/api/img2img';

        // DOM 元素
        const imageUpload = document.getElementById('imageUpload');
        const fileInput = document.getElementById('fileInput');
        const imagePreview = document.getElementById('imagePreview');
        const uploadText = document.getElementById('uploadText');
        const generateBtn = document.getElementById('generateBtn');
        const loading = document.getElementById('loading');
        const resultSection = document.getElementById('resultSection');
        const resultContent = document.getElementById('resultContent');
        const previewSection = document.getElementById('previewSection');
        const previewContent = document.getElementById('previewContent');

        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            setupImageUpload();
            setupFormValidation();
        });

        // 设置图片上传
        function setupImageUpload() {
            // 点击上传
            imageUpload.addEventListener('click', () => {
                fileInput.click();
            });

            // 文件选择
            fileInput.addEventListener('change', handleFileSelect);

            // 拖拽上传
            imageUpload.addEventListener('dragover', (e) => {
                e.preventDefault();
                imageUpload.classList.add('dragover');
            });

            imageUpload.addEventListener('dragleave', () => {
                imageUpload.classList.remove('dragover');
            });

            imageUpload.addEventListener('drop', (e) => {
                e.preventDefault();
                imageUpload.classList.remove('dragover');
                const files = e.dataTransfer.files;
                if (files.length > 0) {
                    handleFile(files[0]);
                }
            });
        }

        // 处理文件选择
        function handleFileSelect(e) {
            const file = e.target.files[0];
            if (file) {
                handleFile(file);
            }
        }

        // 处理文件
        function handleFile(file) {
            if (!file.type.startsWith('image/')) {
                showError('请选择图片文件');
                return;
            }

            const reader = new FileReader();
            reader.onload = function(e) {
                selectedImage = e.target.result;
                imagePreview.src = selectedImage;
                imagePreview.style.display = 'block';
                uploadText.style.display = 'none';
            };
            reader.readAsDataURL(file);
        }

        // 设置表单验证
        function setupFormValidation() {
            const requiredFields = ['modelName', 'prompt'];
            requiredFields.forEach(fieldId => {
                const field = document.getElementById(fieldId);
                field.addEventListener('input', validateForm);
            });
            validateForm();
        }

        // 验证表单
        function validateForm() {
            const modelName = document.getElementById('modelName').value;
            const prompt = document.getElementById('prompt').value.trim();
            const isValid = modelName && prompt && selectedImage;
            
            generateBtn.disabled = !isValid;
            return isValid;
        }

        // 预览参数
        async function previewParameters() {
            if (!validateForm()) {
                showError('请填写必填字段并上传图片');
                return;
            }

            // 显示加载状态
            showLoading(true);
            hidePreview();
            hideResult();

            try {
                // 构建请求参数
                const requestData = buildRequestData();
                
                // 显示预览信息而不发送请求
                showSuccess('参数预览成功！');
                displayPreviewResult(requestData);
            } catch (error) {
                console.error('参数预览请求失败:', error);
                showError(`错误: ${error.message}`);
            } finally {
                showLoading(false);
            }
        }

        // 显示参数预览结果
        function displayPreviewResult(data) {
            const html = `
                <div class="result-grid">
                    <div class="result-item">
                        <h4>📝 提示词参数</h4>
                        <p><strong>正面提示词:</strong> ${data.prompt || '无'}</p>
                        <p><strong>负面提示词:</strong> ${data.negativePrompt || '无'}</p>
                        <p><strong>种子值:</strong> ${data.seed || '随机'}</p>
                    </div>
                    <div class="result-item">
                        <h4>🎨 生成参数</h4>
                        <p><strong>步数:</strong> ${data.steps || '默认'}</p>
                        <p><strong>采样器:</strong> ${data.samplerName || '默认'}</p>
                        <p><strong>调度器:</strong> ${data.scheduler || '默认'}</p>
                        <p><strong>CFG比例:</strong> ${data.cfgScale || '默认'}</p>
                        <p><strong>去噪强度:</strong> ${data.denoisingStrength || '默认'}</p>
                    </div>
                    <div class="result-item">
                        <h4>📐 尺寸参数</h4>
                        <p><strong>宽度:</strong> ${data.width || '默认'}</p>
                        <p><strong>高度:</strong> ${data.height || '默认'}</p>
                        <p><strong>批次大小:</strong> ${data.batchSize || '默认1'}</p>
                    </div>
                    <div class="result-item">
                        <h4>⚙️ 高级参数</h4>
                        <p><strong>恢复面部:</strong> ${data.restoreFaces ? '是' : '否'}</p>
                        <p><strong>平铺模式:</strong> ${data.tiling ? '是' : '否'}</p>
                        <p><strong>模型名称:</strong> ${data.modelName || '无'}</p>
                    </div>
                </div>
                <div class="result-item" style="margin-top: 20px;">
                    <h4>📋 预览信息</h4>
                    <p><strong>API地址:</strong> ${getApiEndpoint(data)}</p>
                    <p><strong>请求方法:</strong> POST</p>
                    <p><strong>Content-Type:</strong> application/json</p>
                </div>
            `;
            
            previewContent.innerHTML = html;
            previewSection.style.display = 'block';
        }

        // 获取对应的API端点
        function getApiEndpoint(data) {
            // 根据模型名称确定使用哪个端点
            const modelName = data.modelName.toLowerCase();
            if (modelName.includes('ghibli')) {
                return `${API_BASE_URL}${API_ENDPOINT}/ghibli`;
            } else {
                return `${API_BASE_URL}${API_ENDPOINT}/anime`;
            }
        }

        // 生成图片
        async function generateImage() {
            if (!validateForm()) {
                showError('请填写必填字段并上传图片');
                return;
            }

            // 显示加载状态
            showLoading(true);
            hidePreview();
            hideResult();

            try {
                // 构建请求参数
                const requestData = buildRequestData();
                
                // 根据模型名称选择不同的API端点
                const apiUrl = getApiEndpoint(requestData);
                
                // 发送请求
                const response = await fetch(apiUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify(requestData)
                });

                const result = await response.json();
                
                if (response.ok && result.code === 200) {
                    showSuccess('图片生成成功！');
                    displayResult(result.data);
                } else {
                    showError(`生成失败: ${result.msg || '未知错误'}`);
                    if (result.data && result.data.errorMessage) {
                        showError(`详细错误: ${result.data.errorMessage}`);
                    }
                }
            } catch (error) {
                console.error('请求失败:', error);
                showError(`网络错误: ${error.message}`);
            } finally {
                showLoading(false);
            }
        }

        // 构建请求数据
        function buildRequestData() {
            const data = {
                modelName: document.getElementById('modelName').value,
                prompt: document.getElementById('prompt').value.trim(),
                initImage: selectedImage
            };

            // 可选参数
            const negativePrompt = document.getElementById('negativePrompt').value.trim();
            if (negativePrompt) {
                data.negativePrompt = negativePrompt;
            }

            const denoisingStrength = parseFloat(document.getElementById('denoisingStrength').value);
            if (!isNaN(denoisingStrength)) {
                data.denoisingStrength = denoisingStrength;
            }

            const width = parseInt(document.getElementById('width').value);
            if (!isNaN(width)) {
                data.width = width;
            }

            const height = parseInt(document.getElementById('height').value);
            if (!isNaN(height)) {
                data.height = height;
            }

            const steps = parseInt(document.getElementById('steps').value);
            if (!isNaN(steps)) {
                data.steps = steps;
            }

            const cfgScale = parseFloat(document.getElementById('cfgScale').value);
            if (!isNaN(cfgScale)) {
                data.cfgScale = cfgScale;
            }

            const seed = document.getElementById('seed').value.trim();
            if (seed && !isNaN(parseInt(seed))) {
                data.seed = parseInt(seed);
            }

            const samplerName = document.getElementById('samplerName').value;
            if (samplerName) {
                data.samplerName = samplerName;
            }

            return data;
        }

        // 显示结果
        function displayResult(data) {
            const html = `
                <div class="result-grid">
                    <div class="result-item">
                        <h4>任务信息</h4>
                        <p><strong>任务ID:</strong> ${data.taskId}</p>
                        <p><strong>状态:</strong> ${data.status}</p>
                        <p><strong>生成时间:</strong> ${data.generationTime}ms</p>
                        <p><strong>模型:</strong> ${data.modelName}</p>
                    </div>
                    <div class="result-item">
                        <h4>生成参数</h4>
                        <p><strong>提示词:</strong> ${data.prompt}</p>
                        <p><strong>负面提示词:</strong> ${data.negativePrompt || '无'}</p>
                        <p><strong>种子:</strong> ${data.seed}</p>
                        <p><strong>步数:</strong> ${data.steps}</p>
                        <p><strong>CFG比例:</strong> ${data.cfgScale}</p>
                        <p><strong>去噪强度:</strong> ${data.denoisingStrength}</p>
                    </div>
                </div>

                <div class="image-result">
                    <h4>原始图片</h4>
                    <img src="${data.initImageUrl}" alt="原始图片" class="generated-image" style="max-height: 300px; margin-bottom: 20px;">
                    
                    <h4>处理过程与结果</h4>
                    <div style="display: flex; flex-wrap: wrap; gap: 20px; justify-content: center;">
                        ${displayProcessedImages(data.imageUrls)}
                    </div>
                </div>
            `;
            
            resultContent.innerHTML = html;
            resultSection.style.display = 'block';
        }

        // 根据图片URL判断类型并显示
        function displayProcessedImages(imageUrls) {
            if (!imageUrls || imageUrls.length === 0) {
                return '<p>暂无生成的图片</p>';
            }
            
            return imageUrls.map(url => {
                let title = "处理图";
                if (url.includes('final_')) {
                    title = "最终结果";
                } else if (url.includes('canny_')) {
                    title = "Canny边缘检测";
                } else if (url.includes('openpose_')) {
                    title = "OpenPose骨架检测";
                }
                
                return `
                    <div style="text-align: center; max-width: 300px;">
                        <h5>${title}</h5>
                        <img src="${url}" alt="${title}" class="generated-image" style="max-width: 100%; max-height: 300px;">
                    </div>
                `;
            }).join('');
        }

        // 健康检查
        async function testHealth() {
            showLoading(true);
            hidePreview();
            hideResult();

            try {
                const response = await fetch(`${API_BASE_URL}${API_ENDPOINT}/health`);
                const result = await response.json();
                
                if (response.ok && result.code === 200) {
                    let statusText = '';
                    
                    if (result.data) {
                        const status = result.data.status || 'unknown';
                        const message = result.data.message || '';
                        
                        statusText = `状态: ${status} - ${message}`;
                    }
                    
                    showSuccess(`服务健康检查通过！${statusText}`);
                } else {
                    showError('服务健康检查失败');
                }
            } catch (error) {
                console.error('健康检查失败:', error);
                showError(`健康检查失败: ${error.message}`);
            } finally {
                showLoading(false);
            }
        }

        // 重置表单
        function resetForm() {
            // 重置图片
            selectedImage = null;
            imagePreview.style.display = 'none';
            uploadText.style.display = 'block';
            fileInput.value = '';

            // 重置表单字段
            document.getElementById('modelName').value = 'anime_style';
            document.getElementById('prompt').value = '';
            document.getElementById('negativePrompt').value = '';
            document.getElementById('denoisingStrength').value = '0.75';
            document.getElementById('width').value = '512';
            document.getElementById('height').value = '512';
            document.getElementById('steps').value = '30';
            document.getElementById('cfgScale').value = '7.0';
            document.getElementById('seed').value = '';
            document.getElementById('samplerName').value = 'DPM++ 2M';

            // 隐藏预览
            hidePreview();
            hideResult();
            validateForm();
        }

        // 显示加载状态
        function showLoading(show) {
            loading.style.display = show ? 'block' : 'none';
            generateBtn.disabled = show;
        }

        // 显示成功消息
        function showSuccess(message) {
            const successDiv = document.createElement('div');
            successDiv.className = 'success';
            successDiv.textContent = message;
            
            // 根据当前显示的内容决定在哪里显示消息
            if (previewSection.style.display === 'block') {
                previewContent.appendChild(successDiv);
            } else {
                resultContent.appendChild(successDiv);
                resultSection.style.display = 'block';
            }
        }

        // 显示错误消息
        function showError(message) {
            const errorDiv = document.createElement('div');
            errorDiv.className = 'error';
            errorDiv.textContent = message;
            
            // 根据当前显示的内容决定在哪里显示消息
            if (previewSection.style.display === 'block') {
                previewContent.appendChild(errorDiv);
            } else {
                resultContent.appendChild(errorDiv);
                resultSection.style.display = 'block';
            }
        }

        // 隐藏预览
        function hidePreview() {
            previewSection.style.display = 'none';
            previewContent.innerHTML = '';
        }

        // 隐藏结果
        function hideResult() {
            resultSection.style.display = 'none';
            resultContent.innerHTML = '';
        }
    </script>
</body>
</html> 